﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
using System.Net.Sockets;
namespace IRCTest
{
    public delegate void IRCMEvent(object sender, IRCMessageEvent e);
    public delegate void IRCUEvent(object sender,IRCUserEvent e);
    public delegate void IRCCEvent(object sender, IRCCommandEvent e);
    public delegate void IRCBEvent(object sender, IRCBasicEvent e);
    public delegate void IRCListEvent(object sender, IRCUserList e);
    public delegate void IRCPmEvent(object sender, IRCPrivMsg e);
    public delegate void IRCKicked(object sender, IRCKick e);

    public class IRCBot : IDisposable
    {
       
        public event IRCMEvent onMessageReceived;
        public event IRCUEvent onUserEvent;
        public event IRCCEvent onCommand;
        public event IRCBEvent onBasicEvent;
        public event IRCListEvent onUserCheck;
        public event BotMessageSend onMessageSend;
        public event IRCPmEvent onPrivMsg;
        public event IRCKicked onKick;
        // Irc server to connect
        
        public static string SERVER = "travian.natar.travianirc.nl";
        // Irc server's port (6667 is default port)
        private static int PORT = 6667;
        private static string USERNAME = "Falco";
        
        private static string Sig = " 8 * :Created and Developed by DProductions";
        private static string USER = "USER Falco" + Sig;
        // Bot's nickname
        private static string NICK = "Falco";
        private static string PASS = "Sk0tchParuche";
        // Channel to join
        private static string CHANNEL1 = "#Druids.pub";
        private static string CHANNEL2 = "#Druids";
        // StreamWriter is declared here so that PingSender can access it

        public String Nick { get { return NICK; } }
        public IRCCommands Actions;
        public static StreamWriter writer;
        private NetworkStream stream;
        private TcpClient irc;
        private string inputLine;
        private StreamReader reader;
        private PingSender ping;

        public IRCBot(String Nick,String Channel1, String Channel2)
        {                      
            NICK = Nick;
            CHANNEL1 = Channel1;
            CHANNEL2 = Channel2;
            USER += Sig;
        }

        public IRCBot(String Nick,String Password, String Channel1, String Channel2)
        {
            //MessageReceived = new ActionOccured();
            NICK = Nick;
            PASS = Password;
            CHANNEL1 = Channel1;
            CHANNEL2 = Channel2;
            USER += Sig;
        }

        public IRCBot(String Nick, String Password,String Username, String Channel1, String Channel2)
        {
            //MessageReceived = new ActionOccured();
            NICK = Nick;
            PASS = Password;
            CHANNEL1 = Channel1;
            CHANNEL2 = Channel2;
            USER = "USER " + Username + Sig;
        }
        public void RUN(){
            try
            {
                this.irc = new TcpClient(SERVER, PORT);
                this.stream = irc.GetStream();
                this.reader = new StreamReader(stream);
                writer = new StreamWriter(stream);
                writer.AutoFlush = true;
                // Start PingSender thread, used to respond to servers' ping requests and keep-alive.
                ping = new PingSender(reader);

                //instantiate and pass corresponding references
                Actions = new IRCCommands(ref reader, ref writer, CHANNEL1, NICK, USER,ChanWatch:CHANNEL2);
                
                Actions.onMessageSend += new BotMessageSend(MessageSend);
                //ping.Start();
               
                while (true)
                {
                    
                    while ((inputLine = reader.ReadLine()) != null)
                    {
                        
                        if (inputLine.StartsWith("PING :")){
                            ping.setResponse(getResponse(inputLine));
                            ping.Start();

                            continue;    
                        }
                        String Code;
                        if (IRCActions.getCode(inputLine, out Code) && onUserCheck != null && Code.Equals("353"))
                        {
                            IRCUserList MemberList = new IRCUserList(inputLine);
                            onUserCheck(this, MemberList);
                        }
                        else if (IRCActions.getReceipient(inputLine).Equals(this.Nick) && onPrivMsg != null)
                        {
                            IRCPrivMsg Message = new IRCPrivMsg(inputLine);
                            onPrivMsg(this, Message);
                        }
                        else
                        //handle the line using the Irccommands class.
                        switch (IRCCommands.getCommand(inputLine,true))
                        {
                            case "NOTICE":
                            case "PRIVMSG":
                                if (!IRCCommands.isCommand(inputLine) && (onMessageReceived != null))
                                {
                                    IRCMessageEvent MEvent = new IRCMessageEvent(inputLine);
                                    onMessageReceived(this, MEvent);
                                    break;
                                }
                                else if (IRCCommands.isCommand(inputLine) && onCommand != null)
                                {
                                    IRCCommandEvent CEvent = new IRCCommandEvent(inputLine);
                                   
                                    onCommand(this, CEvent);
                                    break;
                                }
                                break;
                            case "KICK":
                                if (onKick != null)
                                {
                                    IRCKick KickEvent = new IRCKick(inputLine);
                                    onKick(this, KickEvent);

                                }
                                break;
                            case "JOIN" : 
                            case "PART" :
                            case "QUIT" :
                            case "NICK" : 
                                if (onUserEvent != null && !IRCActions.getUser(inputLine).Equals(NICK))
                                {
                                    IRCUserEvent LEvent = new IRCUserEvent(inputLine);
                                    onUserEvent(this, LEvent);
                                }
                                            break;
                            default:
                                if (onBasicEvent != null)
                                {
                                    IRCBasicEvent BEvent = new IRCBasicEvent(Line: inputLine);
                                    onBasicEvent(this, BEvent);
                                }
                                break;
                        }
                        
                        
                        
                        //Actions.Handle(inputLine);
                        //Console.WriteLine(inputLine);
                        
                    }
                    // Close all streams
                    writer.Close();
                    reader.Close();
                    irc.Close();
                }
            }
            catch (Exception e)
            {
                
                throw new IRCException(e.Message);

            }
        }

        void MessageSend(object IRCBotInstance, MessageSendEvent Event)
        {
            if (onMessageSend != null)
                onMessageSend(IRCBotInstance, Event);
        }

        private String getResponse(String line)
        {
            String[] val = line.Split(' ');
            for (int i = 0; i < val.Length; i++)
                if (val[i].Contains("PING"))
                {
                    if (val[i + 1].IndexOf(':') != -1)
                        return val[i + 1].Substring(1);
                    else
                        return val[i + 1];
                    
                }
            return null;
        }

        public void GetUsers(String Channel)
        {
            Actions.ListUsers(Channel);

        }
        public void Identify()
        {
            try
            {
                Actions.Identify(PASS);
            }
            catch (NullReferenceException Exc)
            {
                throw new IRCException(Exc.InnerException + " Are you running the bot?");
            }
        }

        public void Complete_reg(String Code)
        {   try
            {

                Actions.Complete_reg(Code);
            }
            catch (NullReferenceException Exc)
            {
                throw new IRCException(Exc.InnerException + " Are you running the bot?");
            }
        }

        public void Kill_Ghost()
        {
            try
            {
                Actions.Kill_Ghost(NICK, PASS);
            }
            catch (NullReferenceException Exc)
            {
                throw new IRCException(Exc.InnerException + " Are you running the bot?");
            }
        }

        public void Ask_inv()
        {
            try
            {
                Actions.Ask_inv(Actions.Channel);
                Actions.Ask_inv(Actions.Channel2);
            }
            catch (NullReferenceException Exc)
            {
                throw new IRCException(Exc.InnerException + " Are you running the bot?");
            }
        }

        public void Register(string Email, string Password)
        {
            try
            {
                Actions.Register(Email, PASS);
            }
            catch (NullReferenceException Exc)
            {
                throw new IRCException(Exc.InnerException + " Are you running the bot?");
            }
        }

        public void Join_Chan()
        {
            try
            {
                Actions.Join_Chan(Actions.Channel);
                Actions.Join_Chan(Actions.Channel2);
            }
            catch (NullReferenceException Exc)
            {
                throw new IRCException(Exc.InnerException + " Are you running the bot?");
            }
        }

        public void Dispose()
        {
            ping.Dispose();
            this.reader.Dispose();
            writer.Dispose();
            this.stream.Dispose();
            this.irc.Close();
            
        }
    }
}
